package jus.poc.prodcons.v5;

import java.util.Date;
import java.util.concurrent.locks.*;
import jus.poc.prodcons.*;

;

/**
 * ProdCons gère les échanges à partir des buffers
 *
 * @author Nunes Thomas <nds.thomas@gmail.com>
 * @author Feng Xu <fengxu1989@gmail.com>
 */
public class ProdCons implements Tampon {

    protected int nbBuffer;
    protected Message buffers[];
    protected int enAttente;
    protected int indice;
    protected Lock lock;
    protected Condition prodCond;
    protected Condition consCond;

    public ProdCons(int nbBuffer) {
        this.nbBuffer = nbBuffer;
        buffers = new Message[nbBuffer];
        enAttente = 0;
        indice = 0;

        lock = new ReentrantLock();
        prodCond = lock.newCondition();
        consCond = lock.newCondition();

    }

    @Override
    public int enAttente() {
        return enAttente;
    }

    @Override
    public Message get(_Consommateur arg0) throws Exception,
            InterruptedException {

        Message m;

        lock.lock();
        try {

            while (enAttente == 0) {
                consCond.await();
            }

            m = buffers[mod((indice - enAttente), nbBuffer)];
            ((MessageX) m).setRecu(new Date());
            ((Consommateur) arg0).retraitMessage(m);
            enAttente--;

            if (enAttente <= nbBuffer && enAttente >= 0) {
                prodCond.signalAll();
            }

            return m;

        } finally {
            lock.unlock();
        }

    }

    @Override
    public void put(_Producteur arg0, Message arg1)
            throws Exception, InterruptedException {

        lock.lock();
        try {

            while (enAttente >= nbBuffer) {
                prodCond.await();
            }

            ((MessageX) arg1).setDate(new Date());
            ((Producteur) arg0).deposerMessage(arg1);
            buffers[indice] = arg1;
            indice = (indice + 1) % nbBuffer;
            enAttente++;

            if (enAttente > 0) {
                consCond.signalAll();
            }

        } finally {
            lock.unlock();
        }

    }

    @Override
    public int taille() {
        return nbBuffer;
    }

    private int mod(int a, int b) {
        int res = a % b;
        if (res < 0 && b > 0) {
            res += b;
        }
        return res;
    }
}
